#include "synsharemem.h"

SynShareMem::SynShareMem(Type type,QString memKey, unsigned int size,QString readKey,QObject *parent)
    : QThread(parent)
    , quitFlag(0)
    , mType(type)
    , shareMem(nullptr)
    , mSema_W2R(nullptr)
    , mSema_R2W(nullptr)
    , mShareData(nullptr)
    , mShareSize(size)
{
    //共享数据缓存空间,只有读取模式才需要
    if(mType == eRead_1_sema || mType == eRead_2_sema)
        mShareData = new char[mShareSize];  //不同线程堆空间可共用

    //信号量
    mSema_W2R = new QSystemSemaphore(QString("%1_Write->Read").arg(readKey), 1);
    if(type == eWrite_2_sema || type == eRead_2_sema)
        mSema_R2W = new QSystemSemaphore(QString("%1_Read->Write").arg(readKey), 1);

    //共享内存，依据类型、key、大小创建共享内存块
    bool isWrite = 0;
    if(mType == eWrite_1_sema || mType == eWrite_2_sema)
        isWrite = 1;
    shareMem = creatShareMemory(memKey,size,isWrite);
    if(!shareMem)
        qDebug()<<"share memery creact ERROR!";
}

SynShareMem::~SynShareMem()
{
    quitFlag = 1;
    shareMem->detach();
    if(mSema_W2R != nullptr)
        delete mSema_W2R;
    if(mSema_R2W != nullptr)
        delete mSema_R2W;
    if(mShareData != nullptr)
        delete []mShareData;
}
void SynShareMem::stop()
{
    //下面代码用于子线程保证退出run函数
    quitFlag = 1;
    if(mSema_W2R != nullptr)
        mSema_W2R->release();
    if(mSema_R2W != nullptr)
        mSema_R2W->release();
}

bool SynShareMem::writeMem(char *data, int size)
{
    writeShareMemory(shareMem,data,size);
    mSema_W2R->release();
    return 1;
}

int SynShareMem::readMem(char *data, int size)
{
    int minSize = qMin(size,mShareSize);
    readShareMemory(shareMem,data,minSize);
    return minSize;
}

void SynShareMem::cleanMem(char data)
{
    QByteArray textData(mShareSize,data);
    char *source = textData.data();
    writeMem(source,textData.size());
}

void SynShareMem::run()
{
    if(quitFlag || mType == eWrite_1_sema )
        return ;

    if(mType == eWrite_2_sema)
    {
        qDebug()<<Q_FUNC_INFO<<"run to Write_2 model while(1)......";
        while(1)
        {
            mSema_R2W->acquire();   //等待其他进程读取结束
            if(quitFlag)
                return ;
            emit readFinished();
        }
    }

    //线程循环只负责读取模式
    qDebug()<<Q_FUNC_INFO<<"run to Read model while(1)......";
    while(1)
    {
        //白点操作。信号量，防止没有写入的时候该线程不断循环造成软件卡顿
        mSema_W2R->acquire();
        if(quitFlag)
            return ;
        qDebug()<<Q_FUNC_INFO<<"find write and start to read...";

        //内存操作
        readShareMemory(shareMem,mShareData,mShareSize);

        //黑点操作
        if(mType == eRead_2_sema)
            mSema_R2W->release();

        emit haveRead(mShareData,mShareSize);
    }
}

QSharedMemory* SynShareMem::creatShareMemory(QString key,int size,bool isWrite)    //默认创建内存段
{
    bool haveError = 0;
    QSharedMemory *mem = new QSharedMemory(key,this);
    if(!mem->attach()) //绑定内存块
    {
        //不存在该共享内存块，需要创建建（因为不确定哪一方先运行，这里不分写入与读取）
        //这里需要注意，是否需要加进程间的互斥锁
        mem->lock();
        haveError = !mem->create(size);
        mem->unlock();
    }

    if(haveError)
    {
       qDebug() << Q_FUNC_INFO<<mem->errorString();
       delete mem;
       mem = NULL;
    }
    qDebug()<<Q_FUNC_INFO<<QString("create shareMemory end! isWrite:%1  error:%2")
                            .arg(isWrite)
                            .arg(mem->errorString());
    return mem;
}

void SynShareMem::relaseShareMemory(QSharedMemory* mem)
{
    if(mem == NULL)    return ;
    mem->detach();  //所有进程全部detach后，会自动释放内存块
    delete mem;
    mem = NULL;
    qDebug()<<Q_FUNC_INFO<<"Relase shareMemory end!";
}

bool SynShareMem::writeShareMemory(QSharedMemory* mem,char *data,int size)
{
    if(mem == NULL)    return 0;
    mem->lock();
    char *dest = reinterpret_cast<char *>(mem->data());
    int minSize = qMin(size,mem->size());
    memcpy(dest, data, minSize);
    mem->unlock();
    qDebug()<<Q_FUNC_INFO<<"Write shareMemory end!";
    return 1;
}

void SynShareMem::readShareMemory(QSharedMemory* mem,char *data,uint size)
{
    if(mem == NULL)    return ;
    mem->lock();
    char *source = (char*)mem->constData();
    memcpy(data, source, size);
    mem->unlock();
    qDebug()<<Q_FUNC_INFO<<"Read shareMemory end!";
}


